/**
 * Copyright (C) 2010 Cloudfarming <info@cloudfarming.nl>
 *
 * Licensed under the Eclipse Public License - v 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.eclipse.org/legal/epl-v10.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package nl.cloudfarming.client.model;

import java.beans.PropertyVetoException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.ActionMap;
import org.openide.awt.UndoRedo;
import org.openide.awt.UndoRedo.Manager;
import org.openide.cookies.SaveCookie;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerUtils;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.lookup.AbstractLookup;
import org.openide.util.lookup.InstanceContent;
import org.openide.util.lookup.ProxyLookup;

/**
 * base class for a dataprovider, provides functionality for data usage and change tracking
 *
 * @author Timon Veenstra
 */
public abstract class DataProvider implements ExplorerManager.Provider, Lookup.Provider, BeanService {

    private ExplorerManager explorerManager;
    private Lookup lookup;
    private final InstanceContent content = new InstanceContent();
    private final UndoRedo.Manager undoredoManager = new UndoRedo.Manager();

    protected final void setExplorerManager(ExplorerManager explorerManager, ActionMap actionMap) {
        this.explorerManager = explorerManager;
        this.lookup = new ProxyLookup(ExplorerUtils.createLookup(explorerManager, actionMap), new AbstractLookup(content));
    }

    /**
     * method to initialize the explorer, this method is expected to call setExplorerManager
     */
    protected abstract void initExplorer();

    /*
     * Get the undo/redo-manager.
     * @return Manager The undo/redo-manager
     */
    public Manager getUndoredoManager() {
        return undoredoManager;
    }

    @Override
    public ExplorerManager getExplorerManager() {
        if (this.explorerManager == null) {
            initExplorer();
        }
        return this.explorerManager;
    }

    @Override
    public Lookup getLookup() {
        if (this.lookup == null) {
            initExplorer();
        }
        return this.lookup;
    }

    public void addSaveCookie(SaveCookie saveCookie) {
        content.add(saveCookie);
    }

    public void removeSaveCookie(SaveCookie saveCookie) {
        content.remove(saveCookie);
    }

    /**
     * add a bean to the node selection
     *
     *
     * @param bean Bean to add to the selection
     * @throws BeanNotFoundException when the bean cannot be found in the node hierachy
     */
    public void addBeanToSelection(Bean bean) throws BeanNotFoundException {
        try {
            List<Node> selected = new ArrayList<Node>(Arrays.asList(getExplorerManager().getSelectedNodes()));
            Node node = findNodeForBean(bean,getExplorerManager().getRootContext());
            if (node == null){
                throw new BeanNotFoundException("Bean "+bean.toString()+" not found in node hierarchy of explorerManager "+getExplorerManager().toString());
            }
            
            selected.add(node);
            getExplorerManager().setSelectedNodes(selected.toArray(new Node[]{}));
        } catch (PropertyVetoException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    /**
     * remove a node from the selection
     *
     * @param bean Bean to remove from the selection
     * @throws BeanNotFoundException when the bean cannot be found in the node hierachy
     */
    public void removeBeanFromSelection(Bean bean) throws BeanNotFoundException {
        try {
            List<Node> selected = new ArrayList<Node>(Arrays.asList(getExplorerManager().getSelectedNodes()));

            Node node = findNodeForBean(bean,getExplorerManager().getRootContext());
            if (node == null){
                throw new BeanNotFoundException("Bean "+bean.toString()+" not found in node hierarchy of explorerManager "+getExplorerManager().toString());
            }

            if (selected.contains(node)) {
                selected.remove(node);
                getExplorerManager().setSelectedNodes(selected.toArray(new Node[]{}));
            }
        } catch (PropertyVetoException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    protected Node findNodeForBean(Bean bean, Node node) {
        if (node instanceof BeanNode) {
            Bean b = ((BeanNode) node).getBean();
            if (bean.equals(b)) {
                return node;
            }
        }
        if (node.getChildren() != null) {
            for (Node child : node.getChildren().getNodes()) {
                Node found = findNodeForBean(bean, child);
                if (found != null) {
                    return found;
                }
            }
        }
        return null;
    }
}
